package com.xmim.fulcrum.session;

import com.xmim.core.constants.MessageConstants;
import com.xmim.core.container.*;
import com.xmim.core.container.handler.AbstractHandlerManager;
import com.xmim.core.exception.ContainerException;
import com.xmim.core.exception.SessionrException;
import com.xmim.core.session.SessionListener;
import com.xmim.fulcrum.FulcrumFactory;
import com.xmim.fulcrum.FulcrumUtil;
import com.xmim.fulcrum.node.FulcrumNode;
import com.xmim.fulcrum.node.FulcrumServiceNode;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public abstract class AbstractFulcrumSessionManager<N, H> extends AbstractHandlerManager<N, H> implements FulcrumSessionManager {
    private final Map<Long, FulcrumSession> sessionMap;
    private final List<SessionListener<FulcrumSession>> sessionListeners;
    private final FulcrumFactory factory;
    private final ServerContainer parent;

    private FulcrumServiceNode localNode;

    public AbstractFulcrumSessionManager(FulcrumFactory factory, ServerContainer parent) {
        this.factory = factory;
        this.parent = parent;
        this.sessionMap = new ConcurrentHashMap<>();
        this.sessionListeners = new ArrayList<>();
    }


    @Override
    public AbstractFulcrumSessionManager<N, H> addSessionListener(SessionListener<FulcrumSession> sessionListener) {
        sessionListeners.add(sessionListener);
        return this;
    }


    @Override
    public AbstractFulcrumSessionManager<N, H> addSession(String sessionId, FulcrumSession session) {
        FulcrumUtil.addHandle(this.parent, session);
        FulcrumUtil.triggerAdd(this.sessionListeners, session);
        this.sessionMap.put(Long.valueOf(sessionId), session);
        return this;
    }

    @Override
    public AbstractFulcrumSessionManager<N, H> addSession(String path, FulcrumNode data) throws ContainerException {
        FulcrumServiceNode localNode = getLocalNode();
        if (data.equals(localNode)) {
            log.debug("新增分布式客户端，本地节点, path={}, data={}", path, data);
            return this;
        }
        if (sessionMap.containsKey(data.getId())) {
            sessionMap.get(data.getId()).close();
            sessionMap.remove(data.getId());
            log.debug("新增分布式客户端，节点重复增加, path={}, data={}", path, data);
        }
        FulcrumSession fulcrumSession = this.createSession(this.parent, (FulcrumServiceNode) data);
        FulcrumUtil.addHandle(this.parent, fulcrumSession);
        FulcrumUtil.triggerAdd(this.sessionListeners, fulcrumSession);
        fulcrumSession.start();
        log.debug("新增分布式客户端，连接成功, path={}, data={}", path, data);
        this.sessionMap.put(data.getId(), fulcrumSession);
        return this;
    }


    protected abstract FulcrumSession createSession(ServerContainer parent, FulcrumServiceNode localNode);

    @Override
    public FulcrumSession getSession(String sessionId) {
        FulcrumSession session = this.sessionMap.get(sessionId);
        return FulcrumUtil.triggerFind(sessionListeners, session);
    }

    @Override
    public void destroySession(String sessionId) {
        try {
            FulcrumSession session = this.sessionMap.get(sessionId);
            FulcrumUtil.triggerDestroy(this.sessionListeners, session);
            if (session != null) {
                session.close();
            }
            this.sessionMap.remove(sessionId);
        } catch (SessionrException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void destroySession(FulcrumServiceNode remoteNode) {
        destroySession(remoteNode.getId().toString());
        log.debug("分布式客户端移除,  node={}", remoteNode);
    }

    @Override
    public void writeAllClient(Object obj) {
        this.sessionMap.keySet().stream().forEach(key -> {
                    Long id = getLocalNode().getId();
                    if (!key.equals(id)) {
                        FulcrumSession peerSender = sessionMap.get(key);
                        peerSender.writeAndFlush(obj);
                    }
                }
        );
    }

    @Override
    public boolean hasSession(String sessionId) {
        return sessionMap.containsKey(sessionId);
    }

    @Override
    public void onAdd(String path, FulcrumNode data) {
        try {
            if (data == null) {
                return;
            }
            log.debug("分布式客户端新增，path={}, data={}", path, data);
            this.addSession(path, data);
        } catch (Exception e) {
            log.error("分布式客户端新增失败 path=+" + path + ", data=" + data, e);
        }
    }


    @Override
    public void onRemove(String path, FulcrumNode data) {
        if (data == null) {
            return;
        }
        log.debug("分布式客户端删除, path={}, data={}", path, data);
        destroySession((FulcrumServiceNode) data);
    }

    @Override
    public void onUpdate(String path, FulcrumNode data) {
        if (data == null) {
            return;
        }
        log.debug("分布式客户端更新, path={}, data={}", path, data);
        try {
            this.addSession(path, data);
        } catch (ContainerException e) {
            log.error("分布式客户端更新失败 path=+" + path + ", data=" + data, e);
        }
    }


    @Override
    public Collection<FulcrumSession> senders() {
        return sessionMap.values();
    }

    /**
     * 设置当前节点
     *
     * @param localNode
     */
    @Override
    public void setLocalNode(FulcrumServiceNode localNode) {
        this.localNode = localNode;
    }

    public FulcrumServiceNode getLocalNode() {
        if (localNode == null) {
            throw new IllegalArgumentException(MessageConstants.INVALID_SERVICE_NODE);
        }
        return localNode;
    }

    public FulcrumFactory getFactory() {
        return factory;
    }
}
